<?php

	require_once('../src/prelude.php');


	if (! isset($_GET['type'])) {

		displayXHTMLDocument('ClassExplorer &ndash; Error',
					 		'<h1>ClassExplorer Error</h1>' .
					 		'<p>No type for documentation display given. Set the <em>type</em> attribute in the URL.</p>');
	}
	else {

		$sType = $_GET['type'];
		$sKind = '';
		$sClass = '';
		if (is_built_in($sType))  { $sKind .= 'Built-In '; $sClass .= 'builtin '; }
		if (is_abstract($sType))  { $sKind .= 'Abstract '; $sClass .= 'abstract '; }
		if (is_class($sType))     { $sKind .= 'Class '; $sClass .= 'class '; }
		if (is_interface($sType)) { $sKind .= 'Interface '; $sClass .= 'interface '; }

		$objReflector = new ReflectionClass($sType);

		$sOut = <<<EOF

		<script type="text/javascript">

			function toggleDerived() {

				var objLink = document.getElementById('deriveToggler');
				var objContainer = document.getElementById('documentationContainer');

				if (document.all) {

					// for IE and opera
					var bIsActive = (objLink.className != 'active');

					if (bIsActive) {

						objContainer.className = '';
						objLink.innerText = 'Hide derived features';
						objLink.className = 'active';
					}
					else {

						objContainer.className = 'hide-derived';
						objLink.innerText = 'Show derived features';
						objLink.className = '';
					}
				}
				else {

					// for FF
					var bIsActive = (objContainer.hasAttribute('class'));

					if (bIsActive) {

						objContainer.removeAttribute('class');

						objLink.firstChild.nodeValue = 'Hide derived features';
						objLink.setAttribute('class', 'active');
					}
					else {

						objContainer.setAttribute('class', 'hide-derived');

						objLink.firstChild.nodeValue = 'Show derived features';
						objLink.removeAttribute('class');
					}
				}
			}

		</script>

		<a href="#" id="deriveToggler" class="active" onCLick="toggleDerived()">Hide derived features</a>
EOF;

		$sExtension = '';
		if ($objReflector -> getExtensionName() !== FALSE) {

			$sExtension = ' (from '. $objReflector -> getExtensionName() .' extension)';
		}

		$sOut .= "<div id=\"documentationContainer\"><h1 class=\"$sClass\"><span class=\"kind\">$sKind</span><a href=\"showSource.php?type=$sType#line-". $objReflector -> getStartLine() ."\" target=\"source\">$sType</a>$sExtension</h1>";

		// Hierarchie
		$objParentClass = $objReflector -> getParentClass();
		if ($objParentClass !== FALSE) {

			$sOut .= buildHierarchyView($objReflector);
		}

		// description
		$sDescription = getParsedDocComment($objReflector);
		$sOut .= "<p>$sDescription</p>";

		// Interfaces
		$arrInterfaces = class_implements($sType, FALSE);
		if (count($arrInterfaces) > 0) {

			$sOut .= '<h2>Implemented Interfaces</h2><ul>';

			sort($arrInterfaces);
			foreach ($arrInterfaces as $sInterface) {

				$sDerived = '';
				$sHint = '';
				if ($objParentClass !== FALSE) {

					$arrParentInterfaces = class_implements($objParentClass -> getName(), FALSE);

					if (in_array($sInterface, $arrParentInterfaces)) {

						$sDerived = ' class="derived"';

						$objImplementor = $objParentClass;
						do {

							$objOldImplementor = $objImplementor;
							$objImplementor = $objImplementor -> getParentClass();
						}
						while (is_object($objImplementor) &&
						       $objImplementor -> implementsInterface($sInterface));


						$sHint = ' <span class="hint">[implemented in <a href="frames.php?type='.
						$objOldImplementor -> getName() .'" target="frames">'.
						$objOldImplementor -> getName() .'</a>]</span>';
					}
				}

				$sOut .= '<li'. $sDerived .'><a href="frames.php?type='. $sInterface .'" target="frames">'. $sInterface .'</a>'. $sHint .'</li>';
			}

			$sOut .= '</ul>';
		}


		// constants
		$arrConstants = $objReflector -> getConstants();
		if (count($arrConstants) > 0) {

			ksort($arrConstants);
			$sOut .= '<h2>Constants</h2><dl class="constants">';
			foreach ($arrConstants as $sConstant => $sValue) {

				$sDerived = '';
				$sHint = '';
				if ($objParentClass !== FALSE) {

					$arrParentConstants = $objParentClass -> getConstants();
					$arrParentConstants = array_keys($arrParentConstants);

					if (in_array($sConstant, $arrParentConstants)) {

						$sDerived = ' class="derived"';

						$objDefiner = $objParentClass;
						do {

							$objOldDefiner = $objDefiner;
							$objDefiner = $objDefiner -> getParentClass();
						}
						while(is_object($objDefiner) &&
						      in_array($sConstant, array_keys($objDefiner -> getConstants())));

						$sHint = ' <span class="hint">[defined in <a href="frames.php?type='. $objOldDefiner -> getName() .'" target="frames">'. $objOldDefiner -> getName() .'</a>]</span>';
					}
				}

				$sValue = htmlspecialchars(parseDefaultValue($sValue));

				$sOut .= "<dt$sDerived>$sConstant</dt>";
				$sOut .= "<dd$sDerived>=$sValue$sHint</dd>";
			}
			$sOut .= '</dl>';
		}


		// properties
		$arrProperties = $objReflector -> getProperties();
		usort($arrProperties, create_function('$a, $b',
							'if ($a -> getName() == $b -> getName()) {' .
							'	return 0;' .
							'}' .
							'return ($a -> getName() < $b -> getName()) ? -1 : 1;'));

		if (count($arrProperties) > 0) {

			$sOut .= '<h2>Properties</h2><dl class="methods">';

			foreach ($arrProperties as $objProperty) {

				$sOut .= buildPropertyView($objProperty, $sType);
			}

			$sOut .= "</dl>";
		}



		// constructor
		$objConstructor = $objReflector -> getConstructor();
		if ($objConstructor !== NULL) {

			$sOut .= '<h2>Constructor</h2><dl class="methods">';
			$sOut .= buildMethodView($objConstructor, $sType);
			$sOut .= "</dl>";
		}


		// methods
		$arrMethods = $objReflector -> getMethods();
		usort($arrMethods, create_function('$a, $b',
							'if ($a -> getName() == $b -> getName()) {' .
							'	return 0;' .
							'}' .
							'return ($a -> getName() < $b -> getName()) ? -1 : 1;'));

		if (count($arrMethods) > 0) {

			$sOut .= '<h2>Methods</h2><dl class="methods">';

			foreach ($arrMethods as $objMethod) {

				if ($objConstructor !== NULL &&
				    $objMethod -> getName() == $objConstructor -> getName()) {

					continue;
				}

				$sOut .= buildMethodView($objMethod, $sType);
			}

			$sOut .= "</dl>";
		}

		// subclasses or implementing classes
		if (is_class($sType)) {

			$arrChildClasses = array();
			foreach (get_declared_classes() as $sClass) {

				if (get_parent_class($sClass) == $sType) {

					$arrChildClasses[] = $sClass;
				}
			}

			if (count($arrChildClasses) > 0) {

				sort($arrChildClasses);
				$arrChildClasses = array_unique($arrChildClasses);
				$sOut .= '<h2>Known Subclasses</h2><ul>';

				foreach ($arrChildClasses as $sClass) {

					$sOut .= '<li><a href="frames.php?type='. $sClass .'" target="frames">'. $sClass .'</a></li>';
				}

				$sOut .= '</ul>';
			}

		}
		else {

			$arrImplementingClasses = array();
			foreach (array_merge(get_declared_classes(), get_declared_interfaces()) as $sClass) {

				if (in_array($sType, class_implements($sClass))) {

					// exclude child classes of first implementing class
					$sParentClass = get_parent_class($sClass);
					if (! $sParentClass ||
					    	($sParentClass &&
					    	! in_array($sType, class_implements($sParentClass))
					    	)) {

						$arrImplementingClasses[] = $sClass;
					}
				}
			}

			if (count($arrImplementingClasses) > 0) {

				$arrImplementingClasses = array_unique($arrImplementingClasses);
				sort($arrImplementingClasses);
				$sOut .= '<h2>Classes implementing this interface</h2><ul>';

				foreach ($arrImplementingClasses as $sClass) {

					$sOut .= '<li><a href="frames.php?type='. $sClass .'" target="frames">'. $sClass .'</a></li>';
				}

				$sOut .= '</ul>';
			}
		}


		$sOut .= '</div>'; // end of #documentationContainer

		displayXHTMLDocument("Documentation of $sKind$sType", $sOut);
	}


	function buildPropertyView($objProperty, $sType) {

		// public values of the properties
		$arrVars = get_class_vars($sType);

		$sOut = '';
		$sDeclClass = $objProperty -> getDeclaringClass() -> getName();
		$bDerived = ($sDeclClass != $sType);
		$sClass = '';
		$sSignature = '';
		$sHint = '';

		// signature
		if ($objProperty -> isPublic()) { $sSignature .= 'public '; $sClass .= 'public '; }
		if ($objProperty -> isProtected()) { $sSignature .= 'protected '; $sClass .= 'protected '; }
		if ($objProperty -> isPrivate()) { $sSignature .= 'private '; $sClass .= 'private '; }
		if ($objProperty -> isStatic()) { $sSignature .= 'static '; $sClass .= 'static '; }

		$sSignature .= '<strong>'. $objProperty -> getName() .'</strong>';
		if (isset($arrVars[$objProperty -> getName()])){

 			$sSignature .= ' = '. parseDefaultValue($arrVars[$objProperty -> getName()]);
 		}

		if ($bDerived) {

			$sClass .= 'derived ';
			$sHint .= ' [declared in <a href="frames.php?type='. $sDeclClass .'" target="frames">'. $sDeclClass .'</a>]';
		}

		$sDescription = getParsedDocComment($objProperty);

		$sLinkURL = 'showSource.php?type='. $sDeclClass .'#line-'. $objProperty -> getDeclaringClass() -> getStartLine();

		$sOut .= "<dt class=\"$sClass\"><a href=\"$sLinkURL\" target=\"source\">$sSignature</a>$sHint</dt>";
		$sOut .= "<dd class=\"$sClass\">$sDescription</dd>";

		return $sOut;
	}


	function buildMethodView($objMethod, $sType) {

		$sOut = '';
		$sDeclClass = $objMethod -> getDeclaringClass() -> getName();
		$bDerived = ($sDeclClass != $sType);
		$sClass = '';
		$sParameters = '';
		$sSignature = '';
		$sHint = '';

		// parameters
		$arrParams = array();
		foreach ($objMethod -> getParameters() as $objParam) {

			$sParam = $objParam -> getName();
			if ($objParam -> isArray()) { $sParam = "array $sParam"; }
			if ($objParam -> isPassedByReference()) { $sParam = "& $sParam"; }
			if ($objParam -> isDefaultValueAvailable()) { $sParam .= '='. parseDefaultValue($objParam -> getDefaultValue()); }
			if ($objParam -> isOptional()) { $sParam = "[$sParam]"; }

			$arrParams[] = $sParam;
		}
		$sParameters = implode(', ', $arrParams);

		// method signature
		if ($objMethod -> isPublic()) { $sSignature .= 'public '; $sClass .= 'public '; }
		if ($objMethod -> isProtected()) { $sSignature .= 'protected '; $sClass .= 'protected '; }
		if ($objMethod -> isPrivate()) { $sSignature .= 'private '; $sClass .= 'private '; }
		if ($objMethod -> isStatic()) { $sSignature .= 'static '; $sClass .= 'static '; }
		if ($objMethod -> isAbstract()) { $sSignature .= 'abstract '; $sClass .= 'abstract '; }
		if ($objMethod -> isFinal()) { $sSignature .= 'final '; $sClass .= 'final '; }
		if ($objMethod -> returnsReference()) { $sSignature .= '& '; }

		$sSignature .= '<strong>'. $objMethod -> getName() .'</strong>( '. $sParameters .' )';

		if ($bDerived) {

			$sClass .= 'derived ';
			$sHint .= 'declared in <a href="frames.php?type='. $sDeclClass .'" target="frames">'. $sDeclClass .'</a>';
		}

		// find previously defined methods in class hierarchy
		$arrOverrideList = array();
		$objParent = $objMethod -> getDeclaringClass() -> getParentClass();
		while ($objParent) {

			if ($objParent -> hasMethod($objMethod -> getName())) {

				$arrOverrideList[] = $objParent -> getName();
			}

			$objParent = $objParent -> getParentClass();
		}

		$arrOverrideList2 = array();
		foreach ($arrOverrideList as $sOverrideClass) {

			$arrOverrideList2[] = '<a href="frames.php?type='. $sOverrideClass .'" target="frames">'. $sOverrideClass .'</a>';
		}

		if (count($arrOverrideList2) > 0) {

			// prevent this output if the method is derived and declared otherwhere
			if ($sHint == '') {

				$sHint .= 'overrides method in '. implode(', ', $arrOverrideList2);
			}
		}

		if ($sHint != '') {

			$sHint = " [$sHint]";
		}

		$sDescription = getParsedDocComment($objMethod);

		$sLinkURL = 'showSource.php?type='. $sDeclClass .'#line-'. $objMethod -> getStartLine();

		$sOut .= "<dt class=\"$sClass\"><a href=\"$sLinkURL\" target=\"source\">$sSignature</a>$sHint</dt>";
		$sOut .= "<dd class=\"$sClass\">$sDescription</dd>";

		return $sOut;
	}


	function buildHierarchyView($objClass, $sInnerCode = '') {

		$arrInterfaces = class_implements($objClass -> getName(), FALSE);

		$objParentClass = $objClass -> getParentClass();
		if ($objParentClass !== FALSE) {

			$arrParentInterfaces = class_implements($objParentClass -> getName(), FALSE);
			$arrInterfaces = array_diff($arrInterfaces, $arrParentInterfaces);
		}

		$sOut = '<ul class="hierarchy">';
		$sOut .= '<li><a href="frames.php?type='. $objClass -> getName() .'" target="frames">'. $objClass -> getName() .'</a>';

		$iCountInterfaces = count($arrInterfaces);
		if ($iCountInterfaces > 0) {

			$sOut .= ' (';
			$i = 1;
			foreach ($arrInterfaces as $sInterface) {

				if ($objClass -> implementsInterface($sInterface)) {

					$sOut .= '<a href="frames.php?type='. $sInterface .'" target="frames">'. $sInterface .'</a>';

					if ($i < $iCountInterfaces) {

						$sOut .= ', ';
					}

					$i++;
				}
			}
			$sOut .= ')';

#			$sOut .= ' ('. implode(', ', $arrInterfaces) .')';
		}

		$sOut .= $sInnerCode;
		$sOut .= '</li>';
		$sOut .= '</ul>';

		if ($objParentClass !== FALSE) {

			$sOut = buildHierarchyView($objParentClass, $sOut);
		}


		return $sOut;
	}

?>